<?php
/**
 * Created by PhpStorm
 *
 * @author  : yiyu
 * @time    : 2018/3/18 17:24
 * @desc    : http请求错误处理类
 * @fileName: HttpErrorHandle.php
 */

namespace app\core\handle;

use app\core\log\Log4dd;
use Exception;
use app\core\exception\BusinessException;
use app\core\exception\ErrorCode;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\ThrowableError;
use think\exception\ValidateException;
use think\Log;

class HttpErrorHandle extends Handle
{
    /**
     * 记录非错误日志的异常类
     *
     * @var array
     */
    private $recordNonErrorLog = [
        BusinessException::class, // 自定义封装业务异常
        ValidateException::class, // 参数验证不通过异常
    ];

    /**
     * 校验非error日志
     *
     * @param Exception $exception
     * @return bool
     */
    private function checkExceptionLogNonErrorType(Exception $exception)
    {
        foreach ($this->recordNonErrorLog as $v) {
            if ($exception instanceof $v) {
                return true;
            }
        }

        return false;
    }

    /**
     * 重载日志记录
     *
     * @param Exception $exception
     */
    public function report(Exception $exception)
    {
        if (!$this->isIgnoreReport($exception)) {
            // 收集异常数据
            $data = [
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'message' => $this->getMessage($exception),
                'code' => $this->getCode($exception),
            ];
            $errMsg = $data['message'];
            $log = date('Y-m-d H:i:s') . " 异常信息：[ " . get_class($exception) . ":{$data['code']}:{$data['file']}:{$data['line']} ]\r\n";

            // 校验非error日志
            if ($this->checkExceptionLogNonErrorType($exception)) {
                Log::info($errMsg . "\r\n" . $log);
            } else {
                // error 日志才记录堆栈信息
                //$log .= "\r\n堆栈信息：\r\n" . $exception->getTraceAsString();
                $log .= "堆栈信息：\r\n" . $this->getExceptionTraceAsString($exception->getTrace());
                Log::error($errMsg);
                Log::notice($log);

                \app\base\service\WeChat\MiniProgramSubscribeMessageService::instance()->sendErrorNoticeMsg([
                    'date' => date('Y年m月d日 H:i:s'),
                    'line' => $data['line'],
                    'error' => $errMsg,
                    'file' => $data['file']
                ]);
            }
        }
    }

    /**
     * 统一异常处理(ajax访问和浏览器访问返回结果不同)
     *
     * @param Exception $e
     * @return \think\Response|\think\response\Json
     */
    public function render(Exception $e)
    {
        // 客户端的请求确认返回 json 格式
        $is_client = request()->header(config('client_request_ua_name')) ? true : false;

        // 参数验证错误
        if ($e instanceof ValidateException) {
            return json($e->getError(), 422);
        } // 请求异常
        elseif ($e instanceof HttpException) {
            return response($e->getMessage(), $e->getStatusCode());
            if (request()->isAjax() || $is_client) {
                return response($e->getMessage(), $e->getStatusCode());
            } else {
                return parent::render($e);
            }
        } // 业务异常，不用记录错误日志
        elseif ($e instanceof BusinessException) {
            return webReturn($e->getMessage(), $e->getCode());
            if (request()->isAjax() || $is_client) {
                return webReturn($e->getMessage(), $e->getCode());
            } else {
                return parent::render($e);
            }
        } // 捕获之前（上面）未捕获的异常，并且将错误交由框架处理
        elseif (!($e instanceof ThrowableError)) {
            if (request()->isAjax() || $is_client) {
                return webReturn('系统开小差了，请稍后再试！', ErrorCode::SYSTEM_ERROR);
            } else {
                return parent::render($e);
            }
        }

        //可以在此交由系统处理
        return parent::render($e);
    }

    private function getExceptionTraceAsString($traces)
    {
        $trace_str = "";
        $count = 0;
        foreach ($traces as $frame) {
            $args = "";
            if (isset($frame['args'])) {
                $args = array();
                foreach ($frame['args'] as $arg) {
                    if (is_string($arg)) {
                        $args[] = "'" . $arg . "'";
                    } elseif (is_array($arg)) {
                        $args[] = "Array";
                    } elseif (is_null($arg)) {
                        $args[] = 'NULL';
                    } elseif (is_bool($arg)) {
                        $args[] = ($arg) ? "true" : "false";
                    } elseif (is_object($arg)) {
                        $args[] = get_class($arg);
                    } elseif (is_resource($arg)) {
                        $args[] = get_resource_type($arg);
                    } else {
                        $args[] = $arg;
                    }
                }
                $args = join(", ", $args);
            }
            $trace_str .= sprintf("#%s %s%s: %s%s%s(%s)\n",
                $count,
                ($frame['file'] ?? '[internal function]'),
                (isset($frame['line']) ? '(' . $frame['line'] . ')' : ''),
                ($frame['class'] ?? ''),
                ($frame['type'] ?? ''),
                ($frame['function'] ?? ''),
                $args);
            $count++;
        }

        $trace_str .= '#' . $count . ' {main}';

        return $trace_str;
    }

}